using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Shinkansen.Configuration;

namespace Shinkansen.Support
{
    public class Cryptography
    {
        /// <summary>
        /// Encrypts the specified plain text.
        /// </summary>
        /// <param name="plainText">The plain text.</param>
        /// <returns></returns>
        public byte[] Encrypt (string plainText)
        {
            if (plainText == null)
                throw new ArgumentNullException ("plainText");

            using (RijndaelManaged rm = new RijndaelManaged ())
            {
                ICryptoKeyProvider cryptoProvider = CryptoProviderBuilder.Current.GetCryptoProvider ();

                Encoding  encoding            = Encoding.UTF8;
                byte[]    cryptoKey           = encoding.GetBytes (cryptoProvider.Key.ToCharArray (), 0, rm.KeySize / 8);
                byte[]    iv                  = encoding.GetBytes (cryptoProvider.IV.ToCharArray (), 0, rm.BlockSize / 8);
                byte[]    data                = encoding.GetBytes (plainText);
                byte[]    encryptedBuffer;

                using (MemoryStream ms = new MemoryStream ())
                using (CryptoStream cryptoStream = new CryptoStream (ms, rm.CreateEncryptor (cryptoKey, iv), CryptoStreamMode.Write))
                {
                    cryptoStream.Write (data, 0, data.Length);
                    cryptoStream.FlushFinalBlock ();

                    encryptedBuffer = new byte[ms.Length];
                    ms.Seek (0, SeekOrigin.Begin);
                    ms.Read (encryptedBuffer, 0, encryptedBuffer.Length);

                    cryptoStream.Close ();
                    ms.Close ();
                }

                return encryptedBuffer;
            }
        }

        /// <summary>
        /// Decrypts the specified crypto text.
        /// </summary>
        /// <param name="cryptoText">The crypto text.</param>
        /// <returns></returns>
        public string Decrypt (byte[] cryptoText)
        { 
            if (cryptoText == null)
                throw new ArgumentNullException ("cryptoText");

            using (RijndaelManaged rm = new RijndaelManaged ())
            {
                ICryptoKeyProvider cryptoProvider = CryptoProviderBuilder.Current.GetCryptoProvider ();

                Encoding    encoding  = Encoding.UTF8;
                byte[]      cryptoKey = encoding.GetBytes (cryptoProvider.Key.ToCharArray (), 0, rm.KeySize / 8);
                byte[]      iv        = encoding.GetBytes (cryptoProvider.IV.ToCharArray (), 0, rm.BlockSize / 8);
                string      plainText;

                using (MemoryStream ms = new MemoryStream (cryptoText))
                using (CryptoStream cryptoStream = new CryptoStream (ms, rm.CreateDecryptor (cryptoKey, iv), CryptoStreamMode.Read))
                {
                    ms.Write (cryptoText, 0, cryptoText.Length);
                    ms.Seek (0, SeekOrigin.Begin);

                    plainText = new StreamReader (cryptoStream).ReadToEnd ();

                    cryptoStream.Close ();
                    ms.Close ();
                }

                return plainText;
            }
        }

        /// <summary>
        /// Generates MD5 hash of a textual value.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <returns></returns>
        public string GenerateMD5Hash (string text)
        {
            if (text == null)
                throw new ArgumentNullException ("text");

            using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider ())
            {
                byte[] originalBytes = Encoding.ASCII.GetBytes (text);
                byte[] hashedBytes = md5.ComputeHash (originalBytes);

                return Encoding.UTF8.GetString (hashedBytes);
            }
        }

        /// <summary>
        /// Generates MD5 hash of a numeric value.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <returns></returns>
        public string GenerateMD5Hash (long number)
        {
            return GenerateMD5Hash (number.ToString());
        }
    }
}